package com.smsc.headend.connector.manager;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.connector.bo.AssociationState;
import com.smsc.headend.connector.bo.TaskContext;
import com.smsc.headend.connector.bo.UnwrappedPacket;
import com.smsc.headend.connector.builder.AssociationBuilder;
import com.smsc.headend.connector.builder.AssociationBuilderFactory;
import com.smsc.headend.connector.builder.LoggerBuilder;
import com.smsc.headend.connector.exception.UDISDeviceConException;
import com.smsc.headend.connector.netty.NettyConfigHelper;
import com.smsc.headend.connector.service.*;
import com.smsc.headend.connector.service.asset.AssetService;
import com.smsc.headend.connector.service.protocol.DLMSPacketCodecService;
import com.smsc.headend.connector.utils.Cosem46ByteUtil;
import com.smsc.headend.connector.utils.GxAddrUtil;
import com.smsc.headend.connector.utils.LogFileNameUtil;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.asset.dmo.DLMSConfigDMO;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.asset.entity.SecurityHls;
import com.smsc.headend.module.asset.enums.SecurityLevel;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.task.dto.ConnectorTask;
import gurux.dlms.GXByteBuffer;
import gurux.dlms.GXDLMSClient;
import gurux.dlms.enums.Authentication;
import gurux.dlms.enums.InterfaceType;
import gurux.dlms.enums.Priority;
import gurux.dlms.enums.Security;
import gurux.dlms.secure.GXDLMSSecureClient;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;

import static com.smsc.headend.connector.bo.AssociationState.State;

/**
 * COSEM
 * 电表设备AA获取
 */
@Component
@Slf4j
public class AssociationManager {
    public static final String CONNECTOR_DEVICE_AA_CREATE = "connector:device:aa.create:%s.%s";

    public static final String CONNECTOR_DEVICE_AA_FAIL = "connector:device:aa.failed:%s";


    @Autowired
    AssociationBuilderFactory associationBuilderFactory;
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    TerminalChannelService terminalChannelService;

    ConcurrentHashMap<String, List<AssociationState>> connectStateMap = new ConcurrentHashMap();
    @Autowired
    DLMSPacketCodecService packetCodecService;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    DeviceConnectService deviceConnectService;

    @Autowired
    RedisUtils redisUtils;


    @Autowired
    DLMSPacketCodecService packetDecodeService;

    @Autowired
    DeviceKeyManager deviceKeyManager;

    @Autowired
    AssetService assetService;

    @Autowired
    DLMSPacketCodecService dlmsPacketCodecService;

    @Value("#{${cosem.client.wport.override:{2221:111,2223:111,2224:111,2225:111}}}")
    Map<Long, Integer> wportOverrideInfo;
    /**
     * key : meterId
     * value : aa build fail time;
     */
    WeakHashMap<Long, Long> meterAAFailedTime = new WeakHashMap<>();


    Cache<String, ReentrantLock> associationLockMap = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build();

    @Value("${meter.hls.system.title}")
    String systemTitle;
    @Autowired
    LogFileNameUtil logFileNameUtil;

    Integer releaseTimeout = 300;

    public AssociationState buildApplicationAssociation(String netAddress, String deviceAddress, ComEntity com, Meter meter, Channel channel, ConnectorTask connectorTask) {
        int timeout = 5 * 60;
        if (meter == null) {
            timeout = 30;
        }

        Boolean aaCreatable = redisTemplate.opsForValue().setIfAbsent(String.format(AssociationManager.CONNECTOR_DEVICE_AA_CREATE, com.getComId(), (meter == null ? -1 : meter.getMeterId())), 1, timeout, TimeUnit.SECONDS);
        if (!aaCreatable) {
            log.info("comId aa failed recently: connectTask={}", JSONUtil.toJsonStr(connectorTask));
            return null;
        }

        ReentrantLock deviceAACreateLock = this.getDeviceAALock(com.getComId(), deviceAddress);
        deviceAACreateLock.lock();

        try {
            AssociationState currentNearestState = getApplicationAssociationState(netAddress, deviceAddress, connectorTask.getRoleId());
            if (!this.needRebuildAssociation(currentNearestState, meter, com, connectorTask, netAddress)) {
                return currentNearestState;
            }

            AssociationState associationState = new AssociationState();
            //从内部或者KMS获取设备密钥
            DeviceSAPandKeys deviceConnectKeys = deviceKeyManager.getDeviceKeys(meter, com, connectorTask);
            associationState.setState(State.init);
            associationState.setIcSubDataItemId(deviceConnectKeys.getIcSubDataItemId()).setRoleId(connectorTask.getRoleId())
                    .setReturnPacket(connectorTask.getReturnPacket())
                    .setClient(getDLMSClient(com, meter, deviceConnectKeys, connectorTask.getMeterId(), null))
                    .setDeviceAddress(deviceAddress == null ? "-1" : deviceAddress);
            Integer connectSecurityLevel = getSecurityLevel(deviceConnectKeys);
            AssociationBuilder builder = associationBuilderFactory.getBuilder(connectSecurityLevel, com, meter);
            LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(com), this.getClass()).info("association: start, level={},builder={}, comId={}, device={}", SecurityLevel.parse(connectSecurityLevel).toString(), builder.getClass().toGenericString(), com.getComId(), meter != null ? meter.getMeterId() : "none");
            Long startTime = SystemClock.now();
            terminalChannelService.setChannelLastSendContext(netAddress, new TaskContext().setDeviceAddress(deviceAddress).setComId(com.getComId()));
            builder.buildAssociation(com, meter, channel, associationState);
            LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(com), this.getClass()).info("association: success, comId={}, cost={}ms", com.getComId(), SystemClock.now() - startTime);
            associationState.setState(State.authorized);
            saveAssociationState(netAddress, deviceAddress, associationState);
            return associationState;
        } catch (UDISDeviceConException e) {
            LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(com), this.getClass()).error("association: connectAndSendTask error, comId={}", com.getComId(), e);
            AssociationState associationState = new AssociationState();
            associationState.setExceptionMsg(e.getErrorText());
            associationState.setState(State.invalid);
            return associationState;
        } catch (Exception e) {
            LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(com), this.getClass()).error("association: unexpected error, comId={}", com.getComId(), e);
        } finally {
            deviceAACreateLock.unlock();
        }
        return null;
    }


    private synchronized ReentrantLock getDeviceAALock(Long comId, String deviceAddress) {
        String key = String.format("%s:%s", comId, deviceAddress);
        ReentrantLock lock = associationLockMap.getIfPresent(key);
        if (lock != null) {
            return lock;
        }
        lock = new ReentrantLock();
        associationLockMap.put(key, lock);
        return lock;
    }

    private void saveAssociationState(String netAddress, String deviceAddress, AssociationState associationState) {
        if (deviceAddress == null) {
            deviceAddress = "-1";
        }
        if (!connectStateMap.containsKey(netAddress)) {
            connectStateMap.put(netAddress, new ArrayList<>());
        }
        List<AssociationState> associationStates = connectStateMap.get(netAddress);
        String finalDeviceAddress = deviceAddress;
        AssociationState old = associationStates.stream().filter(p -> StrUtil.compare(p.getDeviceAddress(), finalDeviceAddress, false) == 0).findAny().orElse(null);
        if (old != null) {
            associationStates.remove(old);
        }
        associationStates.add(associationState);
    }

    private Integer getSecurityLevel(DeviceSAPandKeys deviceConnectKeys) {
        Integer securityCode = SecurityLevel.NoSecurity.getCode();
        if (deviceConnectKeys == null) {
            return securityCode;
        }

        if (!StringUtils.isEmpty(deviceConnectKeys.getLlsKey())) {
            securityCode = SecurityLevel.LLS.getCode();
        }

        if (!CollectionUtils.isEmpty(deviceConnectKeys.getHlsKeys())) {
            securityCode = SecurityLevel.HLS.getCode();
        }
        return securityCode;
    }

    public AssociationState getApplicationAssociationState(String netAddress, String deviceAddress, Integer roleId) {
        if (deviceAddress == null) {
            deviceAddress = "-1";
        }
        List<AssociationState> associationStates = connectStateMap.get(netAddress);
        if (associationStates == null) {
            return null;
        }
        String finalDeviceAddress = deviceAddress;

        Stream<AssociationState> associationStateStream = associationStates.stream().filter(p -> p.getDeviceAddress().equalsIgnoreCase(finalDeviceAddress));
        if (roleId != null && roleId > 0) {
            associationStateStream = associationStateStream.filter(p -> p.getRoleId().intValue() == roleId);
        }
        return associationStateStream.findFirst().orElse(null);
    }

    public AssociationState getRunningDeviceApplicationAssociationState(String netAddress, String deviceAddress) {
        if (deviceAddress == null) {
            deviceAddress = "-1";
        }
        List<AssociationState> associationStates = connectStateMap.get(netAddress);
        if (associationStates == null) {
            return null;
        }
        String finalDeviceAddress = deviceAddress;
        return associationStates.stream().filter(p -> p.getDeviceAddress().equalsIgnoreCase(finalDeviceAddress))
                .findAny().orElse(null);
    }

    public boolean removeApplicationAssociation(String netAddress, String deviceAddress) {
        List<AssociationState> associationStates = connectStateMap.get(netAddress);
        AssociationState old = associationStates.stream().filter(p -> StrUtil.compare(p.getDeviceAddress(), deviceAddress, false) == 0).findAny().orElse(null);
        if (old != null) {
            return associationStates.remove(old);
        }
        return true;
    }

    private GXDLMSClient getDLMSClient(ComEntity terminal, Meter meter, DeviceSAPandKeys deviceConnectKeys, Long meterId, Long modelId) throws UnsupportedEncodingException {
        DLMSConfigDMO dlmsConfigDMO = packetCodecService.getDLMSConfigDMO(terminal, meter);
        Authentication authentication = getAuthentication(deviceConnectKeys);
        SecurityHls securityHls = CollectionUtils.isEmpty(deviceConnectKeys.getHlsKeys()) ? null : deviceConnectKeys.getHlsKeys().get(0);
        GXDLMSClient gxdlmsClient = null;
        if (authentication == Authentication.HIGH_GMAC) {
            log.debug("setting cipher");
            gxdlmsClient = new GXDLMSSecureClient();
            ((GXDLMSSecureClient) gxdlmsClient).getCiphering().setSecurity(Security.AUTHENTICATION_ENCRYPTION);
//            if(meter != null && meter.getModelId() == MeterModel.AM300) {
//                ((GXDLMSSecureClient) gxdlmsClient).getCiphering().setSecurity(Security.ENCRYPTION);
//            } else {
//                ((GXDLMSSecureClient) gxdlmsClient).getCiphering().setSecurity(Security.AUTHENTICATION_ENCRYPTION);
//            }
//            byte[] wsSystemTitle = new byte[]{0x57,0x53,0x45,0x00,0x00,0x00,0x00,0x01};
//            ((GXDLMSSecureClient) gxdlmsClient).getCiphering().setSystemTitle(wsSystemTitle);
            ((GXDLMSSecureClient) gxdlmsClient).getCiphering().setSystemTitle(systemTitle.getBytes("ASCII"));
            ((GXDLMSSecureClient) gxdlmsClient).getCiphering().setBlockCipherKey(ByteUtils.hexStringToByte(securityHls.getGuek()));
            ((GXDLMSSecureClient) gxdlmsClient).getCiphering().setAuthenticationKey(ByteUtils.hexStringToByte(securityHls.getGak()));
        } else {
            gxdlmsClient = new GXDLMSClient();
        }
        gxdlmsClient.setPriority(Priority.NORMAL);
        if (dlmsConfigDMO.getInterfaceType().equalsIgnoreCase(InterfaceType.HDLC.name())) {
            if (meter == null) {
                meter = assetService.getMeterById(meterId);
                modelId = meter.getModelId();
            }
            if (Optional.ofNullable(deviceConnectKeys.getWPort()).isPresent()) {
                log.error("wport  null! meterId:{}, comId:{}", meter == null ? "null" : meter.getMeterId(), terminal.getComId());
            }
            gxdlmsClient.setClientAddress(Optional.ofNullable(deviceConnectKeys.getWPort()).orElse(1L).intValue());
            gxdlmsClient.setServerAddress(meter.getDeviceAddr() == null ? 1 : GxAddrUtil.getHDLCServerAddress(GxAddrUtil.setDesId(meter.getDeviceAddr())));
            gxdlmsClient.setInterfaceType(InterfaceType.HDLC);
        } else {
            if (!Optional.ofNullable(deviceConnectKeys.getWPort()).isPresent()) {
                log.error("wport  null! meterId:{}, comId:{}", meter == null ? "null" : meter.getMeterId(), terminal.getComId());
            }
            gxdlmsClient.setClientAddress(Optional.ofNullable(deviceConnectKeys.getWPort()).orElse(1L).intValue());
            gxdlmsClient.setServerAddress(1);
        }
        gxdlmsClient.setUseLogicalNameReferencing(!dlmsConfigDMO.getIsShortName());
        gxdlmsClient.setInterfaceType(InterfaceType.valueOf(dlmsConfigDMO.getInterfaceType()));
        gxdlmsClient.setAuthentication(authentication);
        gxdlmsClient.setMaxReceivePDUSize(dlmsConfigDMO.getMaxPduSize() == null ? 1024 : dlmsConfigDMO.getMaxPduSize());
        byte[] pass = getDLMSPassword(deviceConnectKeys);
        gxdlmsClient.setPassword(pass);
        gxdlmsClient = overwriteClientAddress(modelId, gxdlmsClient);
        return gxdlmsClient;
    }

    private GXDLMSClient overwriteClientAddress(Long modelId, GXDLMSClient gxdlmsClient) {
        if (!wportOverrideInfo.containsKey(modelId)) {
            return gxdlmsClient;
        }
        gxdlmsClient.setClientAddress(wportOverrideInfo.get(modelId));
        return gxdlmsClient;
    }

    private byte[] getDLMSPassword(DeviceSAPandKeys deviceConnectKeys) throws UnsupportedEncodingException {
        byte[] pass = new byte[0];
        if (deviceConnectKeys == null) {
            return null;
        }
        if (StringUtils.isNotEmpty(deviceConnectKeys.getLlsKey())) {
            pass = deviceConnectKeys.getLlsKey().getBytes(StandardCharsets.UTF_8);
        }
        return pass;
    }

    private Authentication getAuthentication(DeviceSAPandKeys deviceConnectKeys) {
        if (deviceConnectKeys == null) {
            return Authentication.NONE;
        }
        if (StringUtils.isNotEmpty(deviceConnectKeys.getLlsKey())) {
            return Authentication.LOW;
        }
        //默认使用GMAC
        if (!CollectionUtils.isEmpty(deviceConnectKeys.getHlsKeys())) {
            return Authentication.HIGH_GMAC;
        }
        return Authentication.NONE;
    }

    public void addAssociationFailCache(Meter meter) {
        if (meter == null || meter.getMeterId() == null || meter.getMeterId() < 0 || ((meterAAFailedTime.containsKey(meter.getMeterId())) && SystemClock.now() - meterAAFailedTime.get(meter.getMeterId()) < 5 * 60 * 1000)) {
            return;
        }
        meterAAFailedTime.put(meter.getMeterId(), SystemClock.now());
    }

    /**
     * @param aaState
     * @param meter
     * @param terminal
     * @param connectorTask
     * @param netAddress    IP (e.g.127.0.0.1)
     * @return
     */
    public boolean needRebuildAssociation(AssociationState aaState, Meter meter, ComEntity terminal, ConnectorTask connectorTask, String netAddress) {
        Boolean result = false;
        if ((connectorTask.getFlag() & 1) == 1 && aaState != null) {
            doRelease(aaState, terminal, netAddress, false);
            result = true;
        }
        if ((connectorTask.getFlag() & 2) == 2) {
            return false;
        }
        //AA在udis已经失效
        if (AssociationState.needRebuild(aaState) == 1) {
            result = true;
        }
        if (AssociationState.needRebuild(aaState) == 2) {
            // do 46 release
            doRelease(aaState, terminal, netAddress, true);
            result = true;
        }
        if (result) {
            redisUtils.del(String.format(AssociationManager.CONNECTOR_DEVICE_AA_CREATE, terminal.getComId(), (meter == null ? -1 : meter.getMeterId())));
        }
        return result;
    }

    private void doRelease(AssociationState aaState, ComEntity terminal, String netAddress, boolean isHdlc) {
        byte[] releasePacket = this.releaseAssociationPacket(aaState, terminal, netAddress, isHdlc);
        final Channel channelByAddress = NettyConfigHelper.getChannelByAddress(netAddress);
        deviceConnectService.sendPacket2Channel(channelByAddress, releasePacket);
        try {
            byte[] bytes = terminalChannelService.getSyncResponse(channelByAddress, 60, aaState.getDeviceAddress());
            LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(terminal), this.getClass()).info("release aa over, release {}", bytes == null ? "over time" : "success");
        } catch (InterruptedException e) {
        }
    }

    private byte[] releaseAssociationPacket(AssociationState aaState, ComEntity terminal, String netAddress, boolean isHdlc) {
        final Logger terminalLogger = LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(terminal), this.getClass());
        byte[] releasePacket = null;
        if (isHdlc) {
            releasePacket = Cosem46ByteUtil.getHdlcReleasePacket(aaState.getClient());
            removeApplicationAssociation(netAddress, aaState.getDeviceAddress());
            return releasePacket;
        }
        try {
            byte[][] sourcePackets = aaState.getClient().releaseRequest();
            if (sourcePackets == null || sourcePackets.length == 0 || sourcePackets[0].length == 0) {
                //GURUX 生成RARQ 不进行下面两步将返回空内容
                aaState.getClient().aarqRequest();
                aaState.getClient().parseAareResponse(new GXByteBuffer(ByteUtils.hexStringToByte("61 29 A1 09 06 07 60 85 74 05 08 01 01 A2 03 02 01 00 A3 05 A1 03 02 01 00 BE 10 04 0E 08 00 06 5F 1F 04 00 00 1E 1D 04 00 00 07")));
                releasePacket = aaState.getClient().releaseRequest()[0];
            } else {
                releasePacket = sourcePackets[0];
            }
            Boolean needWrapDeviceAddress = associationReleaseNeedDeviceAddress(aaState, terminal);
            if (needWrapDeviceAddress) {
                terminalLogger.debug("add EUI to packet comId={}, deviceAddress={}", terminal.getComId(), aaState.getDeviceAddress());
                releasePacket = packetDecodeService.wrapPrefixToPacket(releasePacket, terminal, aaState.getDeviceAddress());
            }
            terminalLogger.info("build release aa packet: {}", ByteUtils.byteToHexString(releasePacket));
        } catch (Exception e) {
            terminalLogger.error("release aa packet init fail; aaState={}", JSONUtil.toJsonStr(aaState), e);
        }
        removeApplicationAssociation(netAddress, aaState.getDeviceAddress());
        return releasePacket;
    }

    private Boolean associationReleaseNeedDeviceAddress(AssociationState aaState, ComEntity terminal) {
        //DCU下AAState对象内deviceAddress为-1的是直接与DCU通讯， dcu下与电表透传则需要传设备地址
        if (terminal.getComDeviceType().longValue() == DeviceType.DCU.getId() && !"-1".equals(aaState.getDeviceAddress())) {
            return true;
        }
        return false;
    }

    public byte[] setAssociationInvalid(ComEntity terminal, String netAddress, String deviceAddress, boolean sendPacket) {
        final AssociationState associationState = this.getRunningDeviceApplicationAssociationState(netAddress, deviceAddress);
        if (associationState == null) {
            return null;
        }
        associationState.setState(AssociationState.State.invalid);
        if (sendPacket) {
            return this.releaseAssociationPacket(associationState, terminal, netAddress, false);
        } else {
            removeApplicationAssociation(netAddress, associationState.getDeviceAddress());
        }
        return null;
    }

    public byte[] getAssociationFirstPacket(String netAddress, String deviceAddress, ComEntity com, Meter meter, ConnectorTask connectorTask) throws UnsupportedEncodingException, UDISDeviceConException {
        Long modelId = null;
        if (meter == null) {
            DLMSConfigDMO dlmsConfigDMO = packetCodecService.getDLMSConfigDMO(com, meter);
            modelId = ((ComConfigDMO) dlmsConfigDMO).getModelId();
        } else {
            modelId = meter.getModelId();
        }
        AssociationState associationState = new AssociationState();
        //从内部或者KMS获取设备密钥
        DeviceSAPandKeys deviceConnectKeys = deviceKeyManager.getDeviceKeys(meter, com, connectorTask);
        associationState.setState(State.authorizing)
                .setIcSubDataItemId(deviceConnectKeys.getIcSubDataItemId())
                .setRoleId(connectorTask.getRoleId())
                .setReturnPacket(connectorTask.getReturnPacket())
                .setClient(getDLMSClient(com, meter, deviceConnectKeys, connectorTask.getMeterId(), modelId))
                .setDeviceAddress(deviceAddress == null ? "-1" : deviceAddress)
                .setModelId(modelId);
        Integer connectSecurityLevel = getSecurityLevel(deviceConnectKeys);
        AssociationBuilder builder = associationBuilderFactory.getBuilder(connectSecurityLevel, com, meter);
        LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(com), this.getClass()).info("association: start,level={},builder={}, comId={}, device={}", SecurityLevel.parse(connectSecurityLevel).toString(), builder.getClass().toGenericString(), com.getComId(), meter != null ? meter.getMeterId() : "none");
        associationState.setBuilder(builder);
        this.saveAssociationState(netAddress, deviceAddress, associationState);
        try {
            byte[] bytes = builder.nextPacket(com, associationState);
            bytes = packetCodecService.wrapPrefixToPacket(bytes, com, associationState.getDeviceAddress());
            return bytes;
        } catch (Exception e) {
            throw new UDISDeviceConException("build aa fail");
        }
    }

    public Boolean isAuthoring(AssociationState associationState) {
        if (associationState == null) {
            return false;
        }
        return State.authorizing.equals(associationState.getState());
    }

    public int packetReceive(Channel channel, ComEntity terminal, AssociationState associationState, byte[] byteReceive) {
        try {
            UnwrappedPacket unwrappedPacket = packetCodecService.unwrapPrefixPacket(byteReceive);
            Boolean hasNext = associationState.getBuilder().acceptPacket(terminal, unwrappedPacket.getPacket(), associationState);
            if (hasNext) {
                byte[] bytes = associationState.getBuilder().nextPacket(terminal, associationState);
                bytes = packetCodecService.wrapPrefixToPacket(bytes, terminal, associationState.getDeviceAddress());
                if (bytes != null) {
                    boolean isHdlc = associationState.getClient().getInterfaceType().equals(InterfaceType.HDLC);
                    ConnectorTask connectorTask = channel.attr(NettyConfigHelper.connectTaskRegular).get();
                    dlmsPacketCodecService.configRetryBO(isHdlc, channel, bytes, connectorTask, associationState.getDeviceAddress());
                    deviceConnectService.sendPacket2Channel(channel, bytes);
                }
                return 0;
            } else {
                associationState.setState(AssociationState.State.authorized);
                return 1;
            }
        } catch (UDISDeviceConException e) {
            associationState.setState(State.invalid);
            LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(terminal), this.getClass()).error("AAFailed:step:{},{}", associationState.getStepNumber(), e.getErrorText());
            return -1;
        } catch (Exception e) {
            associationState.setState(State.invalid);
            LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(terminal), this.getClass()).error("AAFailedForUnexpected:step:{},{}", associationState.getStepNumber(), e.getMessage(), e);
            return -1;
        }
    }
}
